/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.fei.ClassDiagram.Editor.Wrappers;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import org.fei.ClassDiagram.Editor.events.PackageSelectEvent;
import org.fei.ClassDiagram.Editor.events.listeners.PackageRenameListener;
import org.fei.ClassDiagram.ProjectScannerFactory;
import org.fei.ClassDiagram.XMI.XmiIdFactory;
import org.netbeans.modules.classfile.ClassFile;
import org.openide.filesystems.FileObject;
import org.netbeans.modules.classfile.Access;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 *
 * @author Tomas
 */
public class ClassDataWrapper implements PackageRenameListener {

    private ClassFile classFile;
    private FileObject sourceFileObject;  // fileobject of java source file
    private ArrayList<MethodDataWrapper> methods;  // metody (bez konstruktorov)
    private ArrayList<VariableDataWrapper> variables;  // premenne triedy
    private PackageDataWrapper packageData = null;
    private String packageName;
    private String className;
    private String typeSignature;
    private String sourceFileName;
    private int access;
    private boolean publicClass;
    private boolean enumClass;
    private boolean interfaceClass;
    private boolean abstractClass;
    private String xmiid;

    public ClassDataWrapper(ClassFile classFile) {

        this.classFile = classFile;

        packageName = classFile.getName().getPackage();
        className = classFile.getName().getSimpleName();
        typeSignature = classFile.getTypeSignature();
        sourceFileName = classFile.getSourceFileName();
        access = classFile.getAccess();

        interfaceClass = Modifier.isInterface(access);
        enumClass = classFile.isEnum();
        abstractClass = Modifier.isAbstract(access);
        publicClass = Access.isPublic(classFile.getAccess());

        sourceFileObject = ProjectScannerFactory.getProjectScanner().getSourceFileObject(sourceFileName, packageName);

        xmiid = XmiIdFactory.getId();
    }
    
    // konstruktor pre inner class
    public ClassDataWrapper(String name, ClassDataWrapper parent) {
    
        classFile = null;
        
        packageName = parent.getPackageName();
        className = name;
        typeSignature = "";
        sourceFileName = parent.getSourceFileName();
        access = parent.getAccess();
        packageData = parent.getPackageData();
        
        interfaceClass = false;
        enumClass = false;
        abstractClass = false;
        publicClass = false;
        
        sourceFileObject = parent.getSourceFileObject();
        
        methods = new ArrayList<MethodDataWrapper>();
        variables = new ArrayList<VariableDataWrapper>();
        
        xmiid = XmiIdFactory.getId();
    }
    
    // konstruktor pre novu triedu, rozhranie, enum, alebo abstraktnu triedu
    public ClassDataWrapper(FileObject fo, String name, PackageDataWrapper packageData, boolean isAbstract, boolean isEnum, boolean isInterface) {
    
        classFile = null;
        
        this.packageName = packageData.getName();
        className = name;
        typeSignature = "";
        sourceFileName = fo.getName();
        access = Access.PUBLIC;
        
        this.packageData = packageData;
        
        interfaceClass = isInterface;
        enumClass = isEnum;
        abstractClass = isAbstract;
        publicClass = true;
        
        sourceFileObject = fo;
        
        methods = new ArrayList<MethodDataWrapper>();
        variables = new ArrayList<VariableDataWrapper>();
        
        xmiid = XmiIdFactory.getId();
    }

    public ClassFile getClassFile() {
        return this.classFile;
    }

    public FileObject getSourceFileObject() {
        return this.sourceFileObject;
    }

    public void setSourceFileObject(FileObject fo) {
        this.sourceFileObject = fo;
    }

    public String getFullName() {
        return getPackageName() + "." + className;
    }
    
    public String getFullName(boolean withoutDefaultPackage) {
        if (this.packageData.isDefaultPackage() && withoutDefaultPackage) {
            return className;
        }
        else {
            return getFullName();
        }
    }

    public String getSimpleName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;

        if (this.isPublicClass()) {
            sourceFileName = className + ".java";
            sourceFileObject = ProjectScannerFactory.getProjectScanner().getSourceFileObject(getSourceFileName(), packageData.getName());
        }
    }
    
    public String modificatorToString() {
    
        if (isAbstractClass() && !isInterface())
            return "abstract";
        if (isInterface())
            return "interface";
        if (isEnum())
            return "enum";
        else
            return "class";
    }

    public ArrayList<MethodDataWrapper> getMethods() {
        return this.methods;
    }

    public void setMethods(ArrayList<MethodDataWrapper> methods) {
        this.methods = methods;
    }

    public ArrayList<VariableDataWrapper> getVariables() {
        return this.variables;
    }

    public void setVariables(ArrayList<VariableDataWrapper> variables) {
        this.variables = variables;
    }

    /**
     * @return the typeSignature
     */
    public String getTypeSignature() {
        return typeSignature;
    }

    /**
     * @return the access
     */
    public int getAccess() {
        return access;
    }

    public String accessToFullString() {

        if (Access.isPublic(access)) {
            return "public";
        }
        if (Access.isPrivate(access)) {
            return "private";
        }
        if (Access.isProtected(access)) {
            return "protected";
        }

        //package private
        return "package";
    }

    public boolean isPublicClass() {
        return this.publicClass;
    }

    /**
     * @return the packageName
     */
    public String getPackageName() {
        if (packageData != null) {
            return packageData.getName();
        } else {
            return packageName;
        }
    }

    public PackageDataWrapper getPackageData() {
        return packageData;
    }

    public void setPackageData(PackageDataWrapper packageData) {
        this.packageData = packageData;
        this.packageName = packageData.getName();
    }

    @Override
    public void onPackageRename(PackageSelectEvent e) {

        if (packageData == e.getPackageData()) {
            sourceFileObject = ProjectScannerFactory.getProjectScanner().getSourceFileObject(getSourceFileName(), e.getPackageData().getName());
        }
    }

    public Element xmiCreateClassElement(Document document) {

        if (this.isEnum()) {
            return xmiCreateEnumElement(document);
        }
        
        if (this.isInterface()) {
            return xmiCreateInterfaceElement(document);
        }

        Element feature = document.createElement("UML:Classifier.feature");
        Element classElement = document.createElement("UML:Class");

        classElement.setAttribute("xmi.id", this.getXmiid());
        classElement.setAttribute("name", this.getSimpleName());
        classElement.setAttribute("visibility", this.accessToFullString());
        classElement.setAttribute("isSpecification", "false");
        classElement.setAttribute("isAbstract", this.isAbstractClass() ? "true" : "false");
        classElement.setAttribute("isActive", "false");

        for (VariableDataWrapper var : this.variables) {
            feature.appendChild(var.xmiCreateAttributeElement(document));
        }

        for (MethodDataWrapper met : this.methods) {
            if (!met.isConstructor()) {
                feature.appendChild(met.xmiCreateOperationElement(document));
            }
        }

        classElement.appendChild(feature);

        return classElement;
    }

    public Element xmiCreateEnumElement(Document document) {

        Element feature = document.createElement("UML:Classifier.feature");
        Element literal = document.createElement("UML:Enumeration.literal");
        Element enumeration = document.createElement("UML:Enumeration");

        enumeration.setAttribute("xmi.id", this.xmiid);
        enumeration.setAttribute("name", this.getSimpleName());
        enumeration.setAttribute("isSpecification", "false");
        enumeration.setAttribute("isAbstract", "false");

        enumeration.appendChild(literal);

        for (VariableDataWrapper v : this.variables) {
            literal.appendChild(v.xmiCreateEnumerationLiteralElement(document));
        }
        
        for (MethodDataWrapper met : this.methods) {
            if (!met.isConstructor()) {
                feature.appendChild(met.xmiCreateOperationElement(document));
            }
        }
        
        enumeration.appendChild(feature);
        
        return enumeration;
    }

    public Element xmiCreateInterfaceElement(Document document) {
        
        Element feature = document.createElement("UML:Classifier.feature");
        Element interfaceElement = document.createElement("UML:Interface");

        interfaceElement.setAttribute("xmi.id", this.getXmiid());
        interfaceElement.setAttribute("name", this.getSimpleName());
        interfaceElement.setAttribute("visibility", this.accessToFullString());
        interfaceElement.setAttribute("isSpecification", "false");
        interfaceElement.setAttribute("isAbstract", "false");

        for (VariableDataWrapper var : this.variables) {
            feature.appendChild(var.xmiCreateAttributeElement(document));
        }

        for (MethodDataWrapper met : this.methods) {
            if (!met.isConstructor()) {
                feature.appendChild(met.xmiCreateOperationElement(document));
            }
        }

        interfaceElement.appendChild(feature);

        return interfaceElement;
    }

    /**
     * @return the xmiid
     */
    public String getXmiid() {
        return xmiid;
    }

    public boolean isEnum() {
        return this.enumClass;
    }

    public boolean isInterface() {
        return this.interfaceClass;
    }
    
    public void addVariable(VariableDataWrapper var) {
        this.variables.add(var);
    }
    
    public void addMethod(MethodDataWrapper method) {
        this.methods.add(method);
    }

    /**
     * @return the abstractClass
     */
    public boolean isAbstractClass() {
        return abstractClass;
    }

    /**
     * @return the sourceFileName
     */
    public String getSourceFileName() {
        return sourceFileName;
    }
}
